/**
 * 可编辑Panel，该Panel继承自Ext.Panel，包装一个支持拖放事件、支持对子items进行编辑、支持括号操作符的容器。
 * 		items包含2种类型：
 * 			单独一个标签（例：输出编辑框）和
 * 			带有一个附加组件的标签（例：客户标签编辑框）
 * 		使用方法和使用Ext.Panel类似：
 * 		1.创建一个biAdhocNs.UXEditPanel实例，包含3个可选属性
 * 				acceptDrop : 是否接收节点拖放，默认 false。
 * 				ddGroup : DropTarget的ddGroup值，当acceptDrop为true时该属性有效，
 * 						容器将自动加入一个DropTarget用于接收节点的拖放。用户必须重载doOnNotifyDrop
 * 						处理拖放事件。
 * 				hasLabelPlus : 是否有附加组件，默认 true。
 * 		2.为items加入以下属性:
 * 			compType : 'label',
 * 			cls:'availableLot-x'
 * 			如果包含附加组件还必须为该标签加入属性plusComp指向对附加组件的引用 
 * 		3.括号支持：
 * 			左右括号的 compType 值分别为 '(' ,')'。对括号编辑时容器会自动校验语法并提供
 * 		一个删除括号的函数 function removeParenthesis()
 * 	
 * 例子：
 * 		items : {
			acceptDrop : true,
			ddGroup : 'ddGroup',
			hasLabelPlus : true,

			xtype : 'editpanel',
			title : 'Panel2',
			region : 'center',
			height : 500,
			width : 350, 
			doOnNotifyDrop : function(data){
				this.add({
					compType:'label',
					cls:'availableLot-x',
					
					xtype:'splitbutton',
					scale : 'large',
					text: data.node.attributes.text
				});
				this.doLayout();
			}
		}
 * @author chenzhuangwei
 */
biAdhocNs.UXEditPanel = Ext.extend(Ext.Panel, {
	dragComp : null,//当前拖动组件
	shadow : null,
	acceptDrop : false,//是否接收节点拖放
	ddGroup : '',
	hasLabelPlus : true,//标签是否有附加组件
	// private
    afterRender : function(){
    	biAdhocNs.UXEditPanel.superclass.afterRender.call(this); // do sizing calcs last
    	var panel = this;
    	if(panel.acceptDrop){
    		panel.createDropTarget(panel);
		}
		panel.body.addListener('mousedown',panel.doMouseDown,panel);
    },
    /**
     * 创建拖拽区
     */
    createDropTarget : function(panel){
		var panelDropTargetEl =  panel.body.dom;
	    var panelDropTarget = new Ext.dd.DropTarget(panelDropTargetEl, {
	        ddGroup     : Ext.isArray(panel.ddGroup) ? panel.ddGroup[0] : panel.ddGroup,
	        notifyEnter : function(ddSource, e, data) {
	            panel.body.stopFx();
	            panel.body.highlight();
	        	return true ;
	        },
	        notifyDrop  : function(ddSource, e, data){ //处理拖拽事件
                panel.doOnNotifyDrop( data );
	            return true ;
	        }
	   	});
		if(Ext.isArray(panel.ddGroup)){
	  		for(var i = 1; i < panel.ddGroup.length ;i++){
			   panelDropTarget.addToGroup(panel.ddGroup[i]);
	   		}
	   	}
    },
	/**
	 * 处理接收放节点事件，子类必须实现此方法
	 * @param {} data
	 */
	doOnNotifyDrop : function(data){
		// to override this function
	},
	/**
	 * 
	 * @param {} e
	 * @param {} t
	 * @param {} o
	 */
	doMouseDown : function(e, t, o ){
		var comp = this.findCompByPosition(e.getPageX(),e.getPageY());
		if(this.beforeCompMove(comp)){
			this.dragComp = comp;
			this.body.addListener('mousemove',this.doMouseMove,this);
			this.body.addListener('mouseup',this.doMouseUp,this);
			this.dragComp.btnEl.setStyle('cursor','move');
		}
	},
	doMouseMove : function(e, t, o ){
		
		this.makeShadow();//处理shadow
		
    	var xy = e.getXY();
		var cursor = this.getDropPoint(xy[0],xy[1],this.dragComp.compType);
		var m = Ext.fly(this.shadow);
		if(cursor == 0){//插入到最前面
			var target = this.items.itemAt(0);
			m.insertBefore(target.getEl());
		}else{						
			var target = this.items.itemAt(cursor-1);
			m.insertAfter(target.getEl());
		}
	},
	doMouseUp : function(e, t, o ){
		//设置光标
		this.dragComp.btnEl.setStyle('cursor','hand');
		
		//find drop point
		var xy = e.getXY();
		var cursor = this.getDropPoint(xy[0],xy[1],this.dragComp.compType);

		//处理移动组件
		this.moveSomething(cursor);
		
		//删除虚线框
		this.removeShadow();
		
		this.doLayout();
		
		//删除事件监听
		this.body.removeListener('mousemove',this.doMouseMove,this);
		this.body.removeListener('mouseup',this.doMouseUp,this);
	},
	/**
	 * 处理移动组件
	 * @param {} cursor 光标
	 */
	moveSomething : function(cursor){
		//当前位置不做调整
		if( this.isPositionChanged(cursor) ){
			switch(this.dragComp.compType){
				//移动括号
				case '(':
				case ')':
					this.handleMoveParenthesis(this.dragComp,cursor);
					break;
				//移动标签
				case 'label':
					this.handleMoveLabel(this.dragComp,cursor);
					break;
			}
		}		
	},
	/**
	 * 判断新位置相对当前位置是否改变
	 * @param {} cursor
	 * @return {Boolean}
	 */
	isPositionChanged : function(cursor){
		return cursor != this.items.indexOf(this.dragComp)
			&& cursor != this.items.indexOf(this.dragComp) + 1
			&& cursor != this.items.indexOf(this.dragComp.plusComp);
	},
	/**
	 * 判断该组件是否可以移动
	 * @param {} comp
	 * @return {Boolean}
	 */
	beforeCompMove : function(comp){
		return comp && (comp.compType == 'label' 
			|| comp.compType == '(' 
			|| comp.compType == ')');
	},
	/**
	 * 创建拖动时的方框
	 */
	makeShadow : function(){
		if(!this.shadow){
			var el = this.dragComp.getEl();
			var width = el.getWidth();
			var height = el.getHeight();
			if(this.dragComp.plusComp){
				width += (el.getLeft() - this.dragComp.plusComp.getEl().getLeft());
			}
    		this.shadow = Ext.DomHelper.insertHtml("beforeBegin", document.body.firstChild, '<div class="availableLot-x x-ie-label-mirror" style="width:' + width + ';height:'+height+';"></div>');
    	}
	},
	/**
	 * 删除虚线框
	 */
	removeShadow : function(){
		if(this.shadow){
			Ext.fly(this.shadow).remove();
			this.shadow = null;
		}
	},
	/**
	 * 移动标签到指定位置
	 * @param {} labelComp 标签
	 * @param {} cursor 光标
	 */
	handleMoveLabel : function(labelComp,cursor){
		
		//1.在指定位置复制组件
		this.copyLabelToCursor(labelComp,cursor);
		
		//2.如果标签和操作符中间有括号，将括号移动到最前面
		this.moveParenthesisToFront(labelComp,cursor);
		
		//3.删除标签和附加组件
		this.removeLabel(labelComp);
		
		this.doLayout();
	},
	/**
	 * 移动括号到指定位置
	 * @param {} ptComp
	 * @param {} cursor
	 */
	handleMoveParenthesis : function(ptComp,cursor){
		//检查是否可以放到该位置
		var old = this.items.indexOf(ptComp);
		var ps = [];
		for(var i = 0 ; i < this.items.getCount();i++){
			if(i == old) continue;
			if(i == cursor){
				ps.push(ptComp);	
			}
			var o = this.items.itemAt(i);
			if(o.compType == '(' || o.compType == ')'){
				ps.push(o);
			}
		}
		if(cursor >= this.items.getCount()){
			ps.push(ptComp);
		}
		if(!this.matchParenthesis(ps)){
			Ext.Msg.alert('提示','语法出错，不能把括号拖到此位置！');
			return;
		}
		//调整到目标位置
		this.insert(cursor,ptComp.cloneConfig());
		this.remove(ptComp,true);
	},
	/**
	 * 在某位置上复制组件
	 * @param {} labelComp
	 * @param {} cursor
	 */
	copyLabelToCursor : function(labelComp,cursor){
		var plusComp = labelComp.plusComp;
		//复制标签
		var nLabelComp = this.insert(cursor,this.cloneComponent(labelComp));
		//复制操作符
		if(!this.hasLabelPlus) return;
		var first = 0;
		var temp = this.findLabelComp();
		if(temp){
			first = this.items.indexOf(temp);//计算第一个标签索引
		}
		//拖到非第一个节点时复制操作符
		if(cursor > first){
			//使用自己的操作符或者第二个节点的操作符
			plusComp = plusComp || this.findLabelComp(1).plusComp;
			if( plusComp ) {
				var nPlusComp = this.insert(cursor,this.clonePlusComponent(plusComp));
				nLabelComp.plusComp = nPlusComp;
			}
		}
		//拖到最前面，将当前节点操作符作为第一节点操作符
		else{
			var second = this.findLabelComp(1);
			if(second && plusComp){
				var nPlusComp = this.insert(cursor + 1,this.clonePlusComponent(plusComp));
				second.plusComp = nPlusComp;
			}
		}
	},
	/**
	 * 克隆操作符组件，默认使用 Ext.Component.cloneConfig 方法
	 * @param {} plusComp 要克隆的组件
	 * @return {Ext.Component} clone The cloned copy of this component
	 */
	clonePlusComponent : function(comp){
		return comp.cloneConfig({text:comp.text});
	},
	/**
	 * 克隆标签组件，默认使用 Ext.Component.cloneConfig 方法，
	 * 对于部分被修改过的组件属性请重新实现此方法
	 * @param {} comp
	 * @return {}
	 */
	cloneComponent : function(comp){
		return comp.cloneConfig({text:comp.text,attributes:comp.attributes});
	},
	/**
	 * 将标签与操作符中间的括号移动到最前端
	 * @param {} labelComp
	 * @param {} cursor
	 */
	moveParenthesisToFront : function(labelComp,cursor){
		//如果标签和操作符中间有括号，将括号移动到最前面
		var lindex = this.items.indexOf(labelComp);
		while( lindex > 0 
			&& (labelComp.plusComp || cursor < lindex) 
			&& this.items.itemAt(lindex - 1).compType == '('){
			var ptComp = this.items.itemAt(lindex-1);
			this.insert(0,ptComp.cloneConfig());
			this.remove(ptComp,true);
			lindex = this.items.indexOf(labelComp);
		}
	},
	/**
	 * 删除标签，包括绑定的操作符
	 * @param {} labelComp
	 */
	removeLabel : function(labelComp){
  	  	var plusComp = labelComp.plusComp;
		this.remove(labelComp,true);
		if(plusComp){
			this.remove(plusComp,true);
		}else{
			//没有操作符即表示删除的是第一节点，必须同时删除后一节点的操作符，保证语法正确
			var label = this.findLabelComp();
			if(label){
				this.remove(label.plusComp,true);
				label.plusComp = null;
			}
		}
	},
	/**
	 * 通过组件类型和坐标获取拖放位置
	 * @param {} x
	 * @param {} y
	 * @param {} type
	 * @return {}
	 */
	getDropPoint : function(x,y,type){
		var point = this.getDropPosition(x,y);
		switch(type){
			case 'label':
				//标签放的位置只能是cursor = 0 || cursor-1 :right,label
				var previous;
				while(point > 0){
					point--;//向前查找可以放的位置
					previous = this.items.itemAt(point);
					if(previous.compType == 'label' || previous.compType == ')'){
						point = point + 1;//插到当前的前面
						break;
					}
				}
				break;
			case '(':
				//左括号的位置只能是cursor = 0 || cursor :label,left
				var next;
				while(point > 0){
					next = this.items.itemAt(point);
					if(next && (next.compType == 'label' || next.compType == '(')){
						break;
					}
					point--;
				}
				break;
			case ')':
				//右括号的位置只能是cursor != 0 || cursor - 1 :label,right
				var previous;
				while(true){
					point--;//向前查找可以放的位置
					previous = this.items.itemAt(Math.abs(point));
					if(previous.compType == 'label' || previous.compType == ')'){
						point = Math.abs(point) + 1;//插到当前的前面
						break;
					}
				}
				break;
		}
		return point;
	},
	/**
	 * 删除括号对，只需传入一个括号id，容器会自动删除一对括号
	 * @param {} id
	 */
	removeParenthesis : function(id){
		//自动配对括号
		var pair = this.findParenthesisPair(id) || [];
		for(var i = 0; i < pair.length; i++){
			this.remove(pair[i],true);
		}
	},
	/**
	 * 通过左或右括号查找括号对，失败返回null
	 * @param {} pCompId
	 * @return {}
	 */
	findParenthesisPair : function(pCompId){
		var stack = [];//定义一个栈用于存放匹配的括号
		var size = this.items.getCount();
		var object = null;
		for(var i = 0 ; i < size ; i++){
			object = this.items.itemAt(i);
			switch(object.compType){
				case '(':
					stack.push(object);
					break;
				case ')':
					if(stack.length > 0){
						var left = stack.pop();
						if(left.id == pCompId || object.id == pCompId){
							return [left,object];
						}
					}
					else
						return null;
					break;
			}
		}
		return null;
	},
	/**
	 * 匹配括号
	 * @param {} array
	 */
	matchParenthesis : function(array){
		var stack = [];//定义一个栈用于存放匹配的括号
		var size = array.length;
		var object = null;
		for(var i = 0 ; i < size ; i++){
			object = array[i];
			switch(object.compType){
				case '(':
					stack.push(object);
					break;
				case ')':
					if(stack.length > 0)
						stack.pop();
					else
						return false;
					break;
			}
		}
		return stack.length == 0;
	},
	/**
	 * 计算鼠标的坐标获取组件放的位置（索引）
	 * @param {} x
	 * @param {} y
	 * @return {}
	 */
	getDropPosition : function(x,y){
		var size = this.items.getCount();
		var cursor = 0;
		for(var i = 0 ; i < size ; i++){
			var o = this.items.itemAt(i);
			var opos = o.getPosition();
			var width = o.getWidth();
			var height = o.getHeight();//52
			if(x > (opos[0] + width/2) && y > opos[1] && y < opos[1] + height){//52 为组件高度
				cursor = i + 1;
			}
		}
		return cursor;
	},
	/**
	 * 查找编辑器中的第n个标签， 默认第0个，失败返回null
	 * @param {} index
	 * @return {}
	 */
	findLabelComp : function(index){
		return this.findCompByIndex(index,'label');
	},
	/**
	 * 查找编辑器中的第n个组件， 默认第0个标签，失败返回null
	 * @param {} index 第n个组件，默认0
	 * @param {} type 组件类型，默认标签 label
	 * @return {}
	 */
	findCompByIndex : function(index,type){
		index = index || 0;
		type = type || 'label';
		var comp = null;
		for(var i = 0; i<this.items.getCount();i++){
			comp = this.items.itemAt(i);
			if(comp.compType == type){
				if(index == 0)
					return comp;
				else
					index--;
			}
		}
		return null;
	},
	/**
	 * 通过位置查找组件
	 * @param {} x
	 * @param {} y
	 * @return {}
	 */
	findCompByPosition : function(x,y){
		var items = this.items;
		var size = items.getCount();
		for(var i=0 ; i < size ; i++){
			var el = items.itemAt(i).getEl();
			if( el.getX() <= x 
				&& x <= el.getX()+ el.getWidth()
				&& el.getY() <= y 
				&& y <= el.getY()+ el.getHeight() - 18){//-18 解决mouseup冲突问题
					return items.itemAt(i);
			}
		}
		return null;
	}
});

Ext.reg('ux_editpanel', biAdhocNs.UXEditPanel);